package com.cskaoyan.javase.oop3._2inner._6lambda._3callback;

/**
 * Lambda表达式和回调(callback)
 * 需求:
 *      老师决定在课堂上提问小明算术题
 *      小明会计算加法,但是不会其它的
 *          如果小明不会,小明会求助其它同学
 *      小红会乘法
 *      小绿会除法
 *      ...
 *
 * @since 11:13
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        LittleMing lm = new LittleMing();
        System.out.println("现在老师准备提问小明童鞋:");
        System.out.println("先提问加法:");
        lm.answerAddQuestion(10, 10);

        System.out.println("继续提问乘法:");
        lm.answerMultiplyQuestion(10, 10, new LittleRed());

        System.out.println("继续提问除法:");
        lm.answerDivideQuestion(20, 2, new LittleGreen());

        System.out.println("继续提问减法:");
        lm.answerQuestion(10, 2, (a, b) -> a - b);
    }

}

class LittleMing {
    private int add(int a, int b) {
        return a + b;
    }

    /*
        小明回答问题
        这个方法,会传入两个参数
        然后小明回答出结果

        这种在方法中调用另外一个方法来实现功能
        就是最常见的方法调用
     */
    public void answerAddQuestion(int a, int b) {
        System.out.println("小明给出计算的答案:" + add(a, b));
    }

    // 完成计算乘法,需要小红协助
    public void answerMultiplyQuestion(int a, int b, LittleRed lr) {
        System.out.println("小明给出计算的答案:" + lr.multiply(a, b));
    }

    public void answerDivideQuestion(int a, int b, LittleGreen lg) {
        System.out.println("小明给出计算的答案:" + lg.divide(a, b));
    }

    /*
        以上方法在实现功能时,都需要额外传入一个对象
        但是本质上不是为了这个对象
        而是为了对象当中的方法
        所以理论上来说,只要传入一个方法进来,就可以实现功能.
        在计算机编程领域,把这种可以传入另外一个方法的方法,把方法作为参数传递,称之为回调方法(callback)
        Java如何实现回调方法呢?
        显然不可能直接在语法上把一个方法传入另外一个方法
        但是可以间接实现
        用功能接口和Lambda表达式
        因为Lambda表达式就是把一个方法实现作为对象
        传入方法一个Lambda表达式看起来是传入一个对象
        本质上是传入一个方法的实现
        这个方法实现就是回调方法

        使用回调方法的好处是,让代码更加灵活
            可以根据需要传入不同的"方法"实现
            从而实现不同的功能
     */

    public void answerQuestion(int a, int b, ICalc ic) {
        // 接口的实现类传入了一个抽象方法实现,表示一种计算的规则
        System.out.println("小明给出计算的答案:" + ic.calc(a, b));
    }

}

@FunctionalInterface
interface ICalc {
    double calc(double a, double b);
}

class LittleRed {
    public int multiply(int a, int b) {
        return a * b;
    }
}

class LittleGreen {
    public double divide(double a, double b) {
        return a / b;
    }
}